Update plugin ZTools 提供商 v1.0.1#308
Conversation
- feat: ZTools OCR + 翻译提供商插件(截图识别 / 代码翻译 / manage) - chore: 调整插件命名空间 - chore: 调整资源url - feat: 微信 OCR 原生模块支持 macOS(libwxocr.dylib) - feat: 截图识别结果改为独立窗口展示(左图右文 + 拖动缩放)
There was a problem hiding this comment.
Code Review
This pull request refactors the screenshot OCR feature by separating the orchestrator and the result viewer into a multi-page architecture. The main window now coordinates screenshotting and OCR execution, while a new borderless sub-window (screen-ocr-result.html) displays the results with interactive zooming, dragging, and text selection. The review feedback highlights several key improvement opportunities: optimizing performance by caching the plugin logo's file reads and avoiding massive base64 string injection over executeJavaScript (suggesting IndexedDB or localStorage instead), handling asynchronous clipboard errors properly, and capturing pointer events immediately on pointerdown to ensure a smoother dragging experience.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| /** 把数据注入子窗口:调用其 window.__loadScreenOcrResult。 */ | ||
| function injectData( | ||
| win: BrowserWindow.WindowInstance, | ||
| data: { image: string; lines: OcrLine[]; isDark: boolean; logo?: string } | ||
| ): void { | ||
| try { | ||
| // 序列化为安全的 JSON,避免引号/换行破坏 JS 字符串 | ||
| const payload = JSON.stringify(data) | ||
| const code = `window.__loadScreenOcrResult && window.__loadScreenOcrResult(${payload});` | ||
| win.webContents.executeJavaScript(code) | ||
| } catch (_) { | ||
| /* ignore:兜底注入会再试 */ | ||
| } | ||
| } |
There was a problem hiding this comment.
使用 executeJavaScript 注入包含完整截图 Base64 数据(可能达数兆字节)的巨大 JSON 字符串,会导致 Electron 的 IPC 通道和渲染进程主线程在解析、编译和执行该 JS 代码时出现明显的卡顿(Jank)。
建议改进方向:
- 利用
localStorage或IndexedDB共享数据:由于主窗口和子窗口处于同源(Same-Origin),可以在主窗口中将大体积的image和lines写入IndexedDB或localStorage,子窗口启动时自行读取,从而避免通过executeJavaScript传递巨型字符串。 - 检查窗口是否已被销毁:在调用
win.webContents.executeJavaScript之前,建议先检查win && !win.isDestroyed(),避免在窗口意外关闭时引发静默异常。
| // 插件 logo 的 data URI(用于子窗口 <img> 展示图标,注入到渲染层)。 | ||
| pluginLogoDataUrl() { | ||
| try { | ||
| const p = path.join(__dirname, '..', 'logo.png') | ||
| const buf = fs.readFileSync(p) | ||
| return 'data:image/png;base64,' + buf.toString('base64') | ||
| } catch (_) { | ||
| return '' | ||
| } | ||
| }, | ||
|
|
||
| // 插件 logo 的 NativeImage 对象(用于 createBrowserWindow 的 icon)。 | ||
| // Windows 下传字符串路径时任务栏按钮仍按 AppID 取宿主 exe 图标, | ||
| // 传 NativeImage 才能让任务栏真正显示插件图标。 | ||
| pluginLogoNativeImage() { | ||
| try { | ||
| // ZTools 在 preload 提供 require('electron').nativeImage | ||
| const { nativeImage } = require('electron') | ||
| return nativeImage.createFromPath(path.join(__dirname, '..', 'logo.png')) | ||
| } catch (_) { | ||
| return null | ||
| } | ||
| }, |
| function copyText(text: string) { | ||
| if (!text) return | ||
| try { | ||
| navigator.clipboard?.writeText(text) | ||
| } catch (_) { | ||
| /* 子窗口可能无 clipboard 权限,静默失败 */ | ||
| } | ||
| copied.value = text | ||
| if (copiedTimer) window.clearTimeout(copiedTimer) | ||
| copiedTimer = window.setTimeout(() => (copied.value = ''), 1200) | ||
| } |
| function onPointerDown(e: PointerEvent) { | ||
| if (e.button !== 0) return | ||
| pointerActive = true | ||
| didDrag = false | ||
| dragStart = { x: e.clientX, y: e.clientY } | ||
| dragOrigin = { x: offsetX.value, y: offsetY.value } | ||
| } | ||
| function onPointerMove(e: PointerEvent) { | ||
| if (!pointerActive) return | ||
| const dx = e.clientX - dragStart.x | ||
| const dy = e.clientY - dragStart.y | ||
| if (!dragging.value && Math.hypot(dx, dy) < DRAG_THRESHOLD) return | ||
| if (!dragging.value) { | ||
| dragging.value = true | ||
| didDrag = true | ||
| ;(e.currentTarget as HTMLElement).setPointerCapture?.(e.pointerId) | ||
| } | ||
| offsetX.value = dragOrigin.x + dx | ||
| offsetY.value = dragOrigin.y + dy | ||
| } | ||
| function onPointerUp(e: PointerEvent) { | ||
| pointerActive = false | ||
| if (dragging.value) { | ||
| dragging.value = false | ||
| ;(e.currentTarget as HTMLElement).releasePointerCapture?.(e.pointerId) | ||
| } | ||
| } |
插件信息
本次变更
截图 / 演示
自检清单
plugins/f-provider/目录此 PR 由 ztools-plugin-cli 自动管理:每次
ztools publish在分支上追加一个 commit,PR 链接保持不变。